/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2021-2022. All rights reserved.
 *
 */

import { execSync } from 'child_process';
import os from 'os';
import path from 'path';
import process from 'process';

import { isLinux, isMac, isWindows } from '@ohos/hvigor-common';
import fse from 'fs-extra';

import { iconv } from '../../../common/util/iconv/index.js';
import { exit } from '../../boot/hooks/exit.js';
import { HvigorLogger } from '../../log/hvigor-log.js';
import { DEFAULT_DAEMON_REGISTRY_PATH } from '../const/daemon-const.js';
import { DaemonInfo, DaemonState } from '../registry/daemon-info.js';

const _log = HvigorLogger.getLogger('process-info-util');

/**
 * 检查目标进程是否为hvigor守护进程的方法
 * 该方法使用时间约在300ms左右，因此暂时只在时间不敏感的stop-daemon中使用
 *
 * @param {number} pid
 * @returns {boolean}
 */
export function isHvigorDaemonProcess(pid: number): boolean {
  if (pid <= 0) {
    return false;
  }
  try {
    process.kill(pid, 0);
  } catch (e) {
    return false;
  }
  if (isWindows()) {
    const wmicCommand = `${path.resolve('C:\\Windows', 'System32', 'wbem', 'wmic')} process where processid="${pid}" get processid,caption,commandline`;
    if (examineHvigorDaemonProcess(wmicCommand, pid)) {
      return true;
    } else {
      const wmiCommand =
        `powershell "Get-CimInstance -ClassName Win32_Process -Filter 'ProcessId LIKE ${pid}' -Property ProcessId,Caption,commandline"${os.EOL}`;
      if (examineHvigorDaemonProcess(wmiCommand, pid)) {
        return true;
      } else {
        _log.debug(
          `hvigor client: Check process info failed. If process with pid=${pid} exists and it is an hvigor ` +
            'process, please check the WMI function of your environment or starting hvigor with --no-daemon param.',
        );
        return false;
      }
    }
  } else if (isLinux() || isMac()) {
    const command = `ps -fp ${pid}`;
    return examineHvigorDaemonProcess(command, pid);
  }
  return false;
}

function examineHvigorDaemonProcess(command: string, pid: number) {
  const encoding = isWindows() ? 'GBK' : 'utf-8';
  try {
    const result = execSync(command, {
      windowsHide: true,
      encoding: 'buffer',
      stdio: 'pipe',
    });
    const stdout = iconv.decode(<Buffer>result, encoding);
    return stdout.includes(pid.toString()) && stdout.includes('node') && stdout.includes('hvigor') && stdout.includes('daemon-process-boot-script');
  } catch (e: any) {
    if (!e.stderr) {
      _log.warn(e);
      return false;
    }
    const stderr = iconv.decode(<Buffer>e.stderr, encoding);
    _log.warn(stderr);
    return false;
  }
}

/**
 * 通过--stop-daemon命令,关闭所有daemon注册表中的守护进程
 * 在client侧被调用,打印日志使用daemon-client
 */
export function stopDaemons(closeAll: boolean) {
  const daemonInfos: { [p: string]: DaemonInfo } = fse.readJsonSync(DEFAULT_DAEMON_REGISTRY_PATH, {
    encoding: 'utf-8',
  });
  let totalStoppedCount = 0;
  Object.values(daemonInfos).forEach((daemonInfo) => {
    if (daemonInfo.state === DaemonState.STOPPED || daemonInfo.state === DaemonState.BROKEN) {
      return;
    }
    if (!closeAll && path.normalize(daemonInfo.cwdPath) !== path.normalize(process.cwd())) {
      return;
    }
    daemonInfo.state = DaemonState.STOPPED;
    if (killDaemonProcess(daemonInfo)) {
      totalStoppedCount++;
    } else {
      delete daemonInfos[daemonInfo.keyId];
    }
  });
  _log.info(`Stopped ${totalStoppedCount} daemon${totalStoppedCount > 1 ? 's' : ''} in total.`);
  fse.writeJsonSync(DEFAULT_DAEMON_REGISTRY_PATH, daemonInfos);
  exit(0);
}

function killDaemonProcess(daemonInfo: DaemonInfo): boolean {
  if (isHvigorDaemonProcess(daemonInfo.pid)) {
    // 并不会触发daemon process中定义的SIGTERM事件的回调
    process.kill(daemonInfo.pid, 'SIGTERM');
    _log.info(`Stopped daemon pid ${daemonInfo.pid}.`);
    return true;
  } else {
    _log.warn(`process pid ${daemonInfo.pid} is not a hvigor daemon. The process will not be killed and the info will be clear. `);
    return false;
  }
}

/**
 * 通过--status-daemon命令,打印所有daemon注册表中的守护进程信息
 * 在client侧被调用,打印日志使用daemon-client
 */
export function logDaemonInfo() {
  _log.info('PID\tSTATUS\tPORT\tROOT_PATH');
  try {
    const daemonInfos: { [p: string]: DaemonInfo } = fse.readJsonSync(DEFAULT_DAEMON_REGISTRY_PATH, {
      encoding: 'utf-8',
    });
    Object.values(daemonInfos).forEach((daemonInfo) => {
      _log.info(`${daemonInfo.pid}\t${daemonInfo.state}\t${daemonInfo.port}\t${daemonInfo.cwdPath}`);
    });
  } finally {
    exit(0);
  }
}
